#!/usr/bin/python
"""
This demo demonstrates how to embed a matplotlib (mpl) plot
into a PyQt4 GUI application, including:

* Using the navigation toolbar
* Adding data to the plot
* Dynamically modifying the plot's properties
* Processing mpl events
* Saving the plot to a file from a menu

The main goal is to serve as a basis for developing rich PyQt GUI
applications featuring mpl plots (using the mpl OO API).

Eli Bendersky (eliben@gmail.com)
License: this code is in the public domain
Last modified: 19.01.2009
"""
import sys, os, random

import matplotlib
#matplotlib.interactive(True)
#matplotlib.use('TkAgg')
#matplotlib.rcParams['toolbar'] = 'None'

from PyQt4.QtCore import *
from PyQt4.QtGui import *

from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
from matplotlib.figure import Figure

from dicomDOSE import dicomDOSE as dd

#a subcless of pushbutton to emit signal when mouse over event
class FocusEmittingButton(QPushButton):
    def __init__(self, text = None, parent = None, name = None):
        QPushButton.__init__(self, text, parent, name)

    def event(self, e):
        t = e.type()
        # this seems to be necessary because of races with qApp.quit()
        if QPushButton:
            return QPushButton.event(self, e)
        else:
            return 0
        #...
    def enterEvent(self, e):
         #print "PushButton enterEvent"
         self.emit(SIGNAL("enterButton()"))
         return QPushButton.enterEvent(self, e)
         #...
    def leaveEvent(self, e):
         #print "PushButton leaveEvent"
         self.emit(SIGNAL("leaveButton()"))
         return QPushButton.leaveEvent(self, e)


class AppForm(QMainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.setWindowTitle('dicompareDOSE: compare DICOM DOSE files')
        self.create_menu()
        self.create_main_frame()
        self.create_status_bar()
        #self.textbox.setText('1 2 3 4')
        self.on_draw()
        self.dosePrimary = False
        self.doseSecondary = False
        self.doseProfiles = False#for profiles
        self.otherplane = "| Y"

    def open_primaryfile(self):
        self.dosePrimary = dd(1) 
        filename = QFileDialog.getOpenFileName(self, 'Open file','.')
        self.dosePrimary.ReadDICOM(str(filename))
        self.changeDosePlane() # to update settings
        self.fileTwoButton.setEnabled(True)
        self.fileOnePlanButton.setEnabled(True)

    def open_secondaryfile(self):
        self.doseSecondary = dd(2) 
        filename = QFileDialog.getOpenFileName(self, 'Open file','.')
        self.doseSecondary.doseplane=self.dosePrimary.doseplane
        self.doseSecondary.profileDirection=self.dosePrimary.profileDirection
        self.doseSecondary.ReadDICOM(str(filename))
        self.changeDosePlane()
        self.fileTwoPlanButton.setEnabled(True)

    def open_primaryPLANfile(self):
        filename = QFileDialog.getOpenFileName(self, 'Open file','.')
        self.dosePrimary.ReadDICOMPlan(str(filename))

    def open_secondaryPLANfile(self):
        filename = QFileDialog.getOpenFileName(self, 'Open file','.')
        self.doseSecondary.ReadDICOMPlan(str(filename))


    def save_plot(self):
        file_choices = "PNG (*.png)|*.png"
        path = unicode(QFileDialog.getSaveFileName(self,
                        'Save file', '',
                        file_choices))
        if path:
            self.self.canvasPrimary.print_figure(path, dpi=self.dpi)
            self.statusBar().showMessage('Saved to %s' % path, 2000)

    def on_about(self):
        msg = """ doseDICOMpare: 
        * compare DICOM DOSE files
        * Use the matplotlib navigation bar
        * Open DICOM-DOSE file
        * Toggle between dose planes (XY|YZ|XZ) using the radio button 
        * Drag the slider to modify the other dimension
        * Save the plot to a file using the File menu
        """
        QMessageBox.about(self, "About the demo", msg.strip())

    def on_pick(self, event):
        # The event received here is of the type
        # matplotlib.backend_bases.PickEvent
        #
        # It carries lots of information, of which we're using
        # only a small amount here.
        #
        box_points = event.artist.get_bbox().get_points()
        msg = "You've clicked on a bar with coords:\n %s" % box_points

        QMessageBox.information(self, "Click!", msg)

    def setXprofile(self):
        if(self.dosePrimary) :self.dosePrimary.profileDirection="X"
        if(self.doseSecondary) : self.doseSecondary.profileDirection="X"
    
    def setYprofile(self):
        if(self.dosePrimary) :self.dosePrimary.profileDirection="Y"
        if(self.doseSecondary) : self.doseSecondary.profileDirection="Y"
    
    def changeDosePlane(self):
        statustitle = "changed " + "dose plane" 
        plane = self.buttonGroup.checkedButton().text()
        msg = plane + " plane "
        self.statusBar().showMessage(msg)
        self.otherplane = "| Z "
        if (plane == "YZ") : self.otherplane = "| X"
        elif (plane == "XZ") : self.otherplane = "| Y"
        s = self.otherplane + "= 0 mm"
        self.slider_label.setText(s)
        if(self.dosePrimary) :
            self.dosePrimary.doseplane = plane
            self.dosePrimary.TwoDSlice()
            self.dosePrimary.DrawFigure(self.figDosePrimary)
        if(self.doseSecondary) :
            self.doseSecondary.doseplane = plane
            self.doseSecondary.TwoDSlice()
            self.doseSecondary.DrawFigure(self.figDoseSecondary)#need to pass the current figure to plot onto

    def changeSlice(self):
		sliceposition=self.slider.value() / 100.0
		if(self.dosePrimary) :
			self.dosePrimary.offset = sliceposition#needs to be limited to of range in x, y, z
			self.dosePrimary.TwoDSlice()# to extract relevant slice
			self.dosePrimary.DrawFigure(self.figDosePrimary)#need to pass the current figure to plot onto
			s = self.otherplane + "= " + str(int(self.dosePrimary.sliceCoord)) + " mm" #set the label
			self.slider_label.setText(s) #
			if(self.dosePrimary.crossHairsFrozen) : # means we are fixing the profile pos and scrolling thru slices
				self.dosePrimary.DrawProfile(self.figDoseProfiles,0,0)#cross hair coords should be saved
			self.dosePrimary.update(self.figDosePrimary,None)
		if(self.doseSecondary):
			self.doseSecondary.offset = sliceposition#needs to be limited to of range in x, y, z
			self.doseSecondary.TwoDSlice()# to extract relevant slice	
			self.doseSecondary.DrawFigure(self.figDoseSecondary)#need to pass the current figure to plot onto
			if(self.doseSecondary.crossHairsFrozen) : #
				self.doseSecondary.DrawSecondaryProfile(self.figDoseProfiles, 0, 0,self.dosePrimary.profax)
			self.doseSecondary.update(self.figDoseSecondary,None)

    def on_draw(self):
         self.canvasPrimary.draw()

    def onPrimarymove(self,event):#need to plot cross-hairs on figure
        if not event.inaxes: return	
        #if we hve frozen cross hairs (eg to save prifile, just return)
        if(self.dosePrimary.crossHairsFrozen) : return
        self.dosePrimary.update(self.figDosePrimary,event)

        if(self.doseSecondary) : self.doseSecondary.update(self.figDoseSecondary,event)
        #draw primary profile, pass secondary dose profile to

        self.dosePrimary.DrawProfile(self.figDoseProfiles, event.xdata, event.ydata)
        # now update profile text
        otherDirection = "X"

        if(self.dosePrimary.profileDirection == "X") :
            otherDirection = "Y"
            position = event.ydata
        else:
            position = event.xdata
        s = otherDirection + "= " + str(int(position)) + " mm" #set the label
        self.ProfilePosition.setText(s)

        if(self.doseSecondary) : #if secondary dose distribution exists, draw the profile
            #self.doseSecondary.update(self.doseSecondary,event)
            self.doseSecondary.DrawSecondaryProfile(self.figDoseProfiles, event.xdata, event.ydata,self.dosePrimary.profax)

    def onPrimaryleave(self,event):#need to plot cross-hairs on figure
        if not event.inaxes: return
        #if we hve frozen cross hairs (eg to save prifile, just return)
        if(self.dosePrimary.crossHairsFrozen) : return
        self.dosePrimary.HideCrossHairs(self.figDosePrimary)
        if(self.doseSecondary) : self.doseSecondary.HideCrossHairs(self.figDoseSecondary)

    def onPrimaryenter(self,event):#need to plot cross-hairs on figure
		if not event.inaxes: return
		self.showHint("move cross hairs to plot X or Y profiles. click to fix cross hairs and scroll through slices using slider bar")
		self.dosePrimary.crossHairsFrozen=False
		self.dosePrimary.update(self.figDosePrimary,event)
		self.dosePrimary.ShowCrossHairs(self.figDosePrimary)
		if(self.doseSecondary) :
			self.doseSecondary.crossHairsFrozen=False
			self.doseSecondary.update(self.figDoseSecondary,event)
			self.doseSecondary.ShowCrossHairs(self.figDoseSecondary)

    def onPrimaryclick(self,event):#to plot profiles only when mouse click depressed
        if not event.inaxes: return
        #if cross hairs already frozen (ie., user clicked in figure), will unfreeze
        if (self.dosePrimary.crossHairsFrozen) :
            self.dosePrimary.crossHairsFrozen = False
            if(self.doseSecondary) : self.doseSecondary.crossHairsFrozen = False
        #otherwise we freeze the profiles
        else :
            self.dosePrimary.crossHairsFrozen=True
            if(self.doseSecondary) : self.doseSecondary.crossHairsFrozen=True
            self.dosePrimary.SaveCrossHairs(event.xdata,event.ydata)
            if(self.doseSecondary) : self.doseSecondary.SaveCrossHairs(event.xdata,event.ydata)

    def showHint(self,value):
        self.Hint.setText(value)

    def create_main_frame(self):
        self.main_frame = QWidget()
       # self.main_frame.setFixedSize(1300,800);
        # Create the mpl Figure and FigCanvas objects.
        # 5x4 inches, 100 dots-per-inch
        #
        self.dpi = 100
        self.figDosePrimary = Figure()
        self.figDoseSecondary = Figure()
        self.figDoseProfiles = Figure()
        self.figDoseGamma = Figure()

        self.canvasPrimary = FigureCanvas(self.figDosePrimary)
        self.canvasPrimary.setParent(self.main_frame)
        self.canvasPrimary.setFixedSize(600,340);
        cid = self.canvasPrimary.mpl_connect('motion_notify_event', self.onPrimarymove)
        cid = self.canvasPrimary.mpl_connect('axes_leave_event', self.onPrimaryleave)
        cid = self.canvasPrimary.mpl_connect('axes_enter_event', self.onPrimaryenter)
        cid = self.canvasPrimary.mpl_connect('button_press_event', self.onPrimaryclick)

        self.canvasSecondary = FigureCanvas(self.figDoseSecondary)
        self.canvasSecondary.setParent(self.main_frame)
        self.canvasSecondary.setFixedSize(600,340);

        self.canvasTertiary = FigureCanvas(self.figDoseProfiles)
        self.canvasTertiary.setParent(self.main_frame)
        self.canvasTertiary.setFixedSize(600,340);

        self.canvasQuaternary = FigureCanvas(self.figDoseGamma)
        self.canvasQuaternary.setParent(self.main_frame)
        self.canvasQuaternary.setFixedSize(600,340);

        # Create the navigation toolbar, tied to the canvas
        #
        #self.mpl_toolbarPrimary = NavigationToolbar(self.canvasPrimary, self.main_frame)
        #self.mpl_toolbarSecondary = NavigationToolbar(self.canvasSecondary, self.main_frame)
        #self.mpl_toolbarTertiary = NavigationToolbar(self.canvasTertiary, self.main_frame)
        #self.mpl_toolbarQuaternary = NavigationToolbar(self.canvasQuaternary, self.main_frame)

        # Other GUI controls
        #

        self.buttonGroup=QButtonGroup()

        self.radio1=QRadioButton()
        self.radio1.setText("XY")
        self.connect(self.radio1, SIGNAL('clicked()'), self.changeDosePlane)

        self.radio2=QRadioButton()
        self.radio2.setText("YZ")
        self.connect(self.radio2, SIGNAL('clicked()'), self.changeDosePlane)

        self.radio3=QRadioButton()
        self.radio3.setText("XZ")
        self.connect(self.radio3, SIGNAL('clicked()'), self.changeDosePlane)

        self.radio3.setChecked(True)

        self.buttonGroup.addButton(self.radio1)
        self.buttonGroup.addButton(self.radio2)
        self.buttonGroup.addButton(self.radio3)

        self.slider_label = QLabel('| Y = 0 mm')#default plane is XZ

        self.slider = QSlider(Qt.Horizontal)
        self.slider.setRange(1, 100)
        self.slider.setValue(20)
        self.slider.setTracking(True)
        self.slider.setTickPosition(QSlider.TicksBothSides)
        self.connect(self.slider, SIGNAL('valueChanged(int)'), self.changeSlice)

        ########## radio buttons to change profile orientation

        self.buttonGroupBottom=QButtonGroup()

        self.radioX=QRadioButton()
        self.radioX.setText("X profile")
        self.connect(self.radioX, SIGNAL('clicked()'), self.setXprofile)

        self.radioY=QRadioButton()
        self.radioY.setText("Y profile")
        self.connect(self.radioY, SIGNAL('clicked()'), self.setYprofile)

        self.buttonGroupBottom.addButton(self.radioY)
        self.buttonGroupBottom.addButton(self.radioX)
        
        self.radioX.setChecked(True)
        
        ##########
        
        hboxbottom = QHBoxLayout()
        hboxbottom.addWidget(self.radioY)
        hboxbottom.addWidget(self.radioX)

        self.ProfilePosition = QLabel('Y = 0')
        hboxbottom.addWidget(self.ProfilePosition)

        self.Hint = QLabel('see here for hints')
        hboxbottom.addWidget(self.ProfilePosition)

        for w in [ self.radioX, self.radioY, self.ProfilePosition, self.Hint ]:
            hboxbottom.addWidget(w)
            hboxbottom.setAlignment(w, Qt.AlignVCenter)

        #
        # Layout with box sizers
        #

        #create buttons for top of window
        #of type focusemitting, which inherits from pushbutton
        self.fileOneButton = FocusEmittingButton(QIcon("./icons/RD1.xpm"),"Open DOSE file 1")
        self.connect(self.fileOneButton, SIGNAL('clicked()'), self.open_primaryfile)
        self.connect(self.fileOneButton, SIGNAL('enterButton()'),
        lambda hint="click to open the reference DICOM DOSE file RD*.dcm": self.showHint(hint))

        self.fileOnePlanButton = FocusEmittingButton(QIcon("./icons/RD1.xpm"),"Open PLAN file 1")
        self.connect(self.fileOnePlanButton, SIGNAL('clicked()'), self.open_primaryPLANfile)
        self.connect(self.fileOnePlanButton, SIGNAL('enterButton()'),
        lambda hint="open the plan file to set the isocentre": self.showHint(hint))
        self.fileOnePlanButton.setEnabled(False)

        self.fileTwoButton = FocusEmittingButton(QIcon("./icons/RD1.xpm"),"Open DOSE file 2")
        self.connect(self.fileTwoButton, SIGNAL('clicked()'), self.open_secondaryfile)
        self.connect(self.fileTwoButton, SIGNAL('enterButton()'),
        lambda hint="open the second DOSE DICOM file for comparison": self.showHint(hint))
        self.fileTwoButton.setEnabled(False)

        self.fileTwoPlanButton = FocusEmittingButton(QIcon("./icons/RD1.xpm"),"Open PLAN file 1")
        self.connect(self.fileTwoPlanButton, SIGNAL('clicked()'), self.open_secondaryPLANfile)
        self.connect(self.fileTwoPlanButton, SIGNAL('enterButton()'),
        lambda hint="open the 2nd plan file to set the isocentre": self.showHint(hint))
        self.fileTwoPlanButton.setEnabled(False)

        #icon = QIcon("image.png")
        #button.setIcon(icon)

        hboxtop = QHBoxLayout()
        hboxtop.addWidget(self.fileOneButton)
        hboxtop.addWidget(self.fileOnePlanButton)
        hboxtop.addWidget(self.fileTwoButton)
        hboxtop.addWidget(self.fileTwoPlanButton)

        for w in [ self.fileOneButton, self.fileOnePlanButton, self.fileTwoButton, self.fileTwoPlanButton]:
            hboxtop.addWidget(w)
            hboxtop.setAlignment(w, Qt.AlignVCenter)

        hbox = QHBoxLayout()

        for w in [ self.radio1, self.radio2, self.radio3, self.slider_label, self.slider]:
            hbox.addWidget(w)

        grid = QGridLayout()

        grid.addLayout(hboxtop,1,1,1,2)

        grid.addWidget(self.canvasPrimary,2,1)
        #grid.addWidget(self.mpl_toolbarPrimary,3,1)

        grid.addWidget(self.canvasSecondary,2,2)
        #grid.addWidget(self.mpl_toolbarSecondary,3,2)

        grid.addLayout(hbox,4,1)

        grid.addWidget(self.canvasTertiary,5,1)
        #grid.addWidget(self.mpl_toolbarTertiary,6,1)


        grid.addWidget(self.canvasQuaternary,5,2)
        #grid.addWidget(self.mpl_toolbarQuaternary,6,2)
        
        grid.addLayout(hboxbottom,7,1,7,2)

        self.main_frame.setLayout(grid)
        self.setCentralWidget(self.main_frame)

    def create_status_bar(self):
        self.status_text = QLabel("XZ dose plane")#change this depending on what mode we are in. also show filename
        self.statusBar().addWidget(self.status_text, 1)

    def create_menu(self):
        self.file_menu = self.menuBar().addMenu("&File")

        load_file_action = self.create_action("&Save plot",
            shortcut="Ctrl+S", slot=self.save_plot,
            tip="Save the plot")

        open_primarydicomfile_action = self.create_action("Open DICOM file &1",
            shortcut="Ctrl+R", slot=self.open_primaryfile,
            tip="Open reference DICOM DOSE file")

        open_secondarydicomfile_action = self.create_action("Open DICOM file &2",
            shortcut="Ctrl+N", slot=self.open_secondaryfile,
            tip="Open non-reference DICOM DOSE file")

        quit_action = self.create_action("&Quit", slot=self.close,
            shortcut="Ctrl+Q", tip="Close the application")

        self.add_actions(self.file_menu,
            (load_file_action, open_primarydicomfile_action, open_secondarydicomfile_action,None, quit_action))

        self.help_menu = self.menuBar().addMenu("&Help")
        about_action = self.create_action("&About",
            shortcut='F1', slot=self.on_about,
            tip='About the demo')

        self.add_actions(self.help_menu, (about_action,))

    def add_actions(self, target, actions):
        for action in actions:
            if action is None:
                target.addSeparator()
            else:
                target.addAction(action)

    def create_action(  self, text, slot=None, shortcut=None,
                        icon=None, tip=None, checkable=False,
                        signal="triggered()"):
        action = QAction(text, self)
        if icon is not None:
            action.setIcon(QIcon(":/%s.png" % icon))
        if shortcut is not None:
            action.setShortcut(shortcut)
        if tip is not None:
            action.setToolTip(tip)
            action.setStatusTip(tip)
        if slot is not None:
            self.connect(action, SIGNAL(signal), slot)
        if checkable:
            action.setCheckable(True)
        return action


def main():
    app = QApplication(sys.argv)
    form = AppForm()
    form.show()	
    app.exec_()


if __name__ == "__main__":
    main()
